package com.ollobot.authorization.exception;

import com.ollobot.authorization.common.ErrorCode;
import com.ollobot.authorization.common.TimestampResponseEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理器 - 确保所有异常都返回JSON格式响应
 * 适用于前后端分离项目
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理认证异常
     */
    @ExceptionHandler(AuthenticationException.class)
    public ResponseEntity<Map<String, Object>> handleAuthenticationException(
            AuthenticationException ex, HttpServletRequest request) {
        log.warn("Authentication failed: {} for request: {}", ex.getMessage(), request.getRequestURI());
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 401);
        response.put("message", "认证失败: " + ex.getMessage());
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
    }

    /**
     * 处理凭据错误异常
     */
    @ExceptionHandler(BadCredentialsException.class)
    public ResponseEntity<Map<String, Object>> handleBadCredentialsException(
            BadCredentialsException ex, HttpServletRequest request) {
        log.warn("Bad credentials: {} for request: {}", ex.getMessage(), request.getRequestURI());
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 401);
        response.put("message", "用户名或密码错误");
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
    }

    /**
     * 处理访问拒绝异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<Map<String, Object>> handleAccessDeniedException(
            AccessDeniedException ex, HttpServletRequest request) {
        log.warn("Access denied: {} for request: {}", ex.getMessage(), request.getRequestURI());
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 403);
        response.put("message", "权限不足，拒绝访问");
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(response);
    }

    /**
     * 处理参数验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, Object>> handleValidationException(
            MethodArgumentNotValidException ex, HttpServletRequest request) {
        log.warn("Validation failed for request: {}", request.getRequestURI());
        
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 400);
        response.put("message", "参数验证失败");
        response.put("errors", errors);
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

    /**
     * 处理绑定异常
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<Map<String, Object>> handleBindException(
            BindException ex, HttpServletRequest request) {
        log.warn("Bind exception for request: {}", request.getRequestURI());
        
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 400);
        response.put("message", "参数绑定失败");
        response.put("errors", errors);
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

    /**
     * 处理参数类型不匹配异常
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<Map<String, Object>> handleTypeMismatchException(
            MethodArgumentTypeMismatchException ex, HttpServletRequest request) {
        log.warn("Type mismatch for parameter '{}' in request: {}", ex.getName(), request.getRequestURI());
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 400);
        response.put("message", String.format("参数 '%s' 类型不正确", ex.getName()));
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

    /**
     * 处理频率限制异常
     */
    @ExceptionHandler(RateBeyondException.class)
    public ResponseEntity<Map<String, Object>> handleRateBeyondException(
            RateBeyondException ex, HttpServletRequest request) {
        log.warn("Rate limit exceeded for request: {}, remaining seconds: {}", 
                request.getRequestURI(), ex.getRemainingSeconds());
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 429);
        response.put("message", ex.getMessage());
        response.put("remainingSeconds", ex.getRemainingSeconds());
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(response);
    }

    /**
     * 处理邮箱发送异常
     */
    @ExceptionHandler(EmailSendException.class)
    public ResponseEntity<Map<String, Object>> handleEmailSendException(
            EmailSendException ex, HttpServletRequest request) {
        log.error("Email send failed for request: {}, email: {}, error type: {}", 
                request.getRequestURI(), ex.getEmail(), ex.getErrorType(), ex);
        
        // 根据错误类型确定HTTP状态码和业务错误码
        HttpStatus httpStatus;
        int businessCode;
        
        switch (ex.getErrorType()) {
            case EmailSendException.ErrorType.INVALID_EMAIL:
                httpStatus = HttpStatus.BAD_REQUEST;
                businessCode = ErrorCode.EMAIL_INVALID;
                break;
            case EmailSendException.ErrorType.EMAIL_NOT_REACHABLE:
                httpStatus = HttpStatus.BAD_REQUEST;
                businessCode = ErrorCode.EMAIL_NOT_REACHABLE;
                break;
            case EmailSendException.ErrorType.SMTP_ERROR:
                httpStatus = HttpStatus.SERVICE_UNAVAILABLE;
                businessCode = ErrorCode.SMTP_ERROR;
                break;
            case EmailSendException.ErrorType.AUTHENTICATION_FAILED:
                httpStatus = HttpStatus.SERVICE_UNAVAILABLE;
                businessCode = ErrorCode.EMAIL_AUTHENTICATION_FAILED;
                break;
            case EmailSendException.ErrorType.NETWORK_ERROR:
                httpStatus = HttpStatus.SERVICE_UNAVAILABLE;
                businessCode = ErrorCode.EMAIL_NETWORK_ERROR;
                break;
            case EmailSendException.ErrorType.TEMPLATE_ERROR:
                httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
                businessCode = ErrorCode.EMAIL_TEMPLATE_ERROR;
                break;
            default:
                httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
                businessCode = ErrorCode.EMAIL_SEND_FAILED;
                break;
        }
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", businessCode);
        response.put("message", ex.getMessage());
        response.put("email", ex.getEmail());
        response.put("errorType", ex.getErrorType());
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(httpStatus).body(response);
    }

    /**
     * 处理业务异常
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Map<String, Object>> handleRuntimeException(
            RuntimeException ex, HttpServletRequest request) {
        log.error("Runtime exception for request: {}", request.getRequestURI(), ex);
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 500);
        response.put("message", ex.getMessage() != null ? ex.getMessage() : "服务器内部错误");
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }

    /**
     * 处理所有其他异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> handleGenericException(
            Exception ex, HttpServletRequest request) {
        log.error("Unexpected exception for request: {}", request.getRequestURI(), ex);
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", false);
        response.put("code", 500);
        response.put("message", "服务器内部错误");
        response.put("timestamp", System.currentTimeMillis());
        response.put("path", request.getRequestURI());
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }
}
